// XMLLib version 1 - ProjectSorter example
//
// Author
//
//   Thomas Koos (info@muetze1.de)
//   www.muetze1.de
//
// License
//
//   This Source Code Form is subject to the terms of the Mozilla Public
//   License, v. 1.1. If a copy of the MPL was not distributed with this
//   file, You can obtain one at http://mozilla.org/MPL/1.1/.
//
// Revision
//
//   $Id: SorterMain.pas 15 2014-04-21 21:20:02Z muetze1@gmail.com $
//

unit SorterMain;

interface

uses
  SorterConfig;                         // for TSorterConfig

function SortProject(const aFilename: string; const aConfig: TSorterConfig): boolean;

implementation

uses
  StrAdapterUTF_8,                      // for UTF-8 encoding
  StrAdapterISO8859_1,                  // for ISO8859-1 encoding
  StrAdapterWindows1252,                // for Windows-1252 encoding
  XMLLib,                               // for TXMLLib
  SysUtils;                             // for AnsiCompareStr()

function OwnNodeCompare(const aNode1, aNode2: TXMLNode; const aData: longword): integer;
var
  lAttr1, lAttr2: TXMLAttribute;
  lIndex: integer;
begin
    // both nodes given?
  if assigned(aNode1) and assigned(aNode2) then
  begin
      // compare node names
    result := AnsiCompareStr(aNode1.Name, aNode2.Name);

      // both equal?
    if 0 = result then
    begin
      lAttr1 := nil;
      lAttr2 := nil;
      lIndex := 0;

        // find an attribute which both nodes have
      while ( lIndex < aNode1.Attributes.Count ) and not assigned(lAttr2) do
      begin
        lAttr1 := aNode1.Attributes[lIndex];
        lAttr2 := aNode2.Attributes.AttributeByName[lAttr1.Name];

        Inc(lIndex);
      end;

        // attribute found on both nodes?
      if assigned(lAttr1) and assigned(lAttr2) then
      begin
          // then sort on attribute value
        result := AnsiCompareStr(lAttr1.Value.AsString, lAttr2.Value.AsString);
      end;
    end;
  end
  else if assigned(ANode1) then
    result := +1
  else
    result := -1;
end;

procedure SortItems(const aNode: TXMLNode; const aConfig: TSorterConfig);
var
  lNode: TXMLNode;
begin
    // valid node given?
  if assigned(aNode) then
  begin
      // honor configuration
    if aNode.Level >= aConfig.StartSortingAtLevel then
    begin
        // sort attributes?
      if aConfig.SortAttributes and aNode.HasAttributes then
        aNode.Attributes.Sort();

        // sort nodes?
      if aConfig.SortNodes then
      begin
          // sort by attribute value on equal nodes?
        if aConfig.SortNodesUseAttrValueOnEqualNodes then
          aNode.Nodes.Sort(OwnNodeCompare)
        else
          aNode.Nodes.Sort();
      end;
    end;

      // go down recursively...
    for lNode in aNode.Nodes do
      SortItems(lNode, aConfig);
  end;
end;

  // sort the given project file
function SortProject(const aFilename: string; const aConfig: TSorterConfig): boolean;
var
  lProject: TXMLLib;
begin
  Result := false;

  try
      // project file
    lProject := TXMLLib.Create;
    try
        // option needed by root attributes
      lProject.Options.AllowAttrOnRootNode := true;
        // option needed to keep the build configuration conditions
      lProject.Options.StringAdapter.ExcludeApostroph := true;

        // load success?
      if lProject.LoadFromFile(aFilename) then
      begin
          // any sorting action to do?
        if aConfig.SortNodes or aConfig.SortAttributes then
        begin
          SortItems(lProject.Root, aConfig);
        end;

          // overall success only including writing
        result := lProject.SaveToFile(aFilename);
      end;
    finally
      lProject.Free;
    end;
  except
    result := false;
  end;
end;

end.
